## @file
# This is the code that goes from real-mode to protected mode.
# It consumes the reset vector, calls TempRamInit API from FSP binary.
#
# @copyright
#  INTEL CONFIDENTIAL
#  Copyright 2014 - 2016 Intel Corporation.
#
#  The source code contained or described herein and all documents related to the
#  source code ("Material") are owned by Intel Corporation or its suppliers or
#  licensors. Title to the Material remains with Intel Corporation or its suppliers
#  and licensors. The Material may contain trade secrets and proprietary and
#  confidential information of Intel Corporation and its suppliers and licensors,
#  and is protected by worldwide copyright and trade secret laws and treaty
#  provisions. No part of the Material may be used, copied, reproduced, modified,
#  published, uploaded, posted, transmitted, distributed, or disclosed in any way
#  without Intel's prior express written permission.
#
#  No license under any patent, copyright, trade secret or other intellectual
#  property right is granted to or conferred upon you by disclosure or delivery
#  of the Materials, either expressly, by implication, inducement, estoppel or
#  otherwise. Any license under such intellectual property rights must be
#  express and approved by Intel in writing.
#
#  Unless otherwise agreed by Intel in writing, you may not remove or alter
#  this notice or any other notice embedded in Materials by Intel or
#  Intel's suppliers or licensors in any way.
#
#  This file contains a 'Sample Driver' and is licensed as such under the terms
#  of your license agreement with Intel or your vendor. This file may be modified
#  by the user, subject to the additional terms of the license agreement.
#
#@par Specification Reference:
##

#include "Fsp.h"

ASM_GLOBAL    ASM_PFX(_gPcd_FixedAtBuild_PcdFsptBaseAddress)
ASM_GLOBAL    ASM_PFX(_gPcd_FixedAtBuild_PcdFspTemporaryRamSize)

ASM_GLOBAL ASM_PFX(_TEXT_REALMODE)
ASM_PFX(_TEXT_REALMODE):
#----------------------------------------------------------------------------
#
# Procedure:    _ModuleEntryPoint
#
# Input:        None
#
# Output:       None
#
# Destroys:     Assume all registers
#
# Description:
#
#   Transition to non-paged flat-model protected mode from a
#   hard-coded GDT that provides exactly two descriptors.
#   This is a bare bones transition to protected mode only
#   used for a while in PEI and possibly DXE.
#
#   After enabling protected mode, a far jump is executed to
#   transfer to PEI using the newly loaded GDT.
#
# Return:       None
#
#  MMX Usage:
#              MM0 = BIST State
#              MM5 = Save time-stamp counter value high32bit
#              MM6 = Save time-stamp counter value low32bit.
#
#----------------------------------------------------------------------------

ASM_GLOBAL ASM_PFX(_ModuleEntryPoint)
ASM_PFX(_ModuleEntryPoint):
  fninit                                # clear any pending Floating point exceptions
  #
  # Store the BIST value in mm0
  #
  movd    %eax, %mm0
  cli

  #
  # Check INIT# is asserted by port 0xCF9
  #
  mov     $0x0CF9, %dx
  in      %dx, %al
  cmp     $0x04, %al
  jnz     NotWarmStart

  #
  # @note Issue warm reset, since if CPU only reset is issued not all MSRs are restored to their defaults
  #
  movw    $0x0CF9, %dx
  movb    $0x06, %al
  out     %al, %dx

  #
  # Save time-stamp counter value
  # rdtsc load 64bit time-stamp counter to EDX:EAX
  #
  rdtsc
  movd    %edx, %mm5
  movd    %ecx, %mm6

  #
  # Load the GDT table in GdtDesc
  #
  movl    $GdtDesc, %esi
  .byte   0x66
  lgdt    %cs:(%si)

  #
  # Transition to 16 bit protected mode
  #
  movl    %cr0, %eax                 # Get control register 0
  orl     $0x00000003, %eax          # Set PE bit (bit #0) & MP bit (bit #1)
  movl    %eax, %cr0                 # Activate protected mode

  movl    %cr4, %eax                 # Get control register 4
  orl     $0x00000600, %eax          # Set OSFXSR bit (bit #9) & OSXMMEXCPT bit (bit #10)
  movl    %eax, %cr4

  #
  # Now we're in 16 bit protected mode
  # Set up the selectors for 32 bit protected mode entry
  #
  mov     $SYS_DATA_SEL, %ax
  mov     %ax, %ds
  mov     %ax, %es
  mov     %ax, %fs
  mov     %ax, %gs
  mov     %ax, %ss

  #
  # Transition to Flat 32 bit protected mode
  # The jump to a far pointer causes the transition to 32 bit mode
  #
  movl    $ProtectedModeEntryLinearAddress, %esi
  jmp     *%cs:(%si)

ASM_GLOBAL ASM_PFX(_TEXT_PROTECTED_MODE)
ASM_PFX(_TEXT_PROTECTED_MODE):

#----------------------------------------------------------------------------
#
# Procedure:    ProtectedModeEntryPoint
#
# Input:        None
#
# Output:       None
#
# Destroys:     Assume all registers
#
# Description:
#
# This function handles:
#   Call two basic APIs from FSP binary
#   Initializes stack with some early data (BIST, PEI entry, etc)
#
# Return:       None
#
#----------------------------------------------------------------------------

ASM_GLOBAL ASM_PFX(ProtectedModeEntryPoint)
ASM_PFX(ProtectedModeEntryPoint):

  # Find the fsp info header
  movl PcdGet32(PcdFsptBaseAddress), %edi

  movl $FVH_SIGINATURE_OFFSET(%edi), %eax
  cmp  $FVH_SIGINATURE_VALID_VALUE, %eax
  jnz  FspHeaderNotFound

  xorl %eax, %eax
  movw $FVH_EXTHEADER_OFFSET_OFFSET(%edi), %ax
  cmp  $0x00, %ax
  jnz  FspFvExtHeaderExist

  xorl %eax, %eax
  movw $FVH_HEADER_LENGTH_OFFSET(%edi), %ax   # Bypass Fv Header
  addl %eax, %edi
  jmp  FspCheckFfsHeader

FspFvExtHeaderExist:
  addl %eax, %edi
  movl $FVH_EXTHEADER_SIZE_OFFSET(%edi), %eax  # Bypass Ext Fv Header
  addl %eax, %edi

  # Round up to 8 byte alignment
  movl %edi, %eax
  andb $0x07, %al
  jz FspCheckFfsHeader

  and  $0xFFFFFFF8, %edi
  add  $0x08, %edi

FspCheckFfsHeader:
  # Check the ffs guid
  movl (%edi), %eax
  cmp  $FSP_HEADER_GUID_DWORD1, %eax
  jnz  FspHeaderNotFound

  movl 0x4(%edi), %eax
  cmp  $FSP_HEADER_GUID_DWORD2, %eax
  jnz  FspHeaderNotFound

  movl 0x08(%edi), %eax
  cmp  $FSP_HEADER_GUID_DWORD3, %eax
  jnz  FspHeaderNotFound

  movl 0x0c(%edi), %eax
  cmp  $FSP_HEADER_GUID_DWORD4, %eax
  jnz  FspHeaderNotFound

  add  $FFS_HEADER_SIZE_VALUE, %edi        # Bypass the ffs header

  # Check the section type as raw section
  movb $SECTION_HEADER_TYPE_OFFSET(%edi), %al
  cmp  $0x19, %al
  jnz  FspHeaderNotFound

  addl $RAW_SECTION_HEADER_SIZE_VALUE, %edi  # Bypass the section header
  jmp  FspHeaderFound

FspHeaderNotFound:
  jmp  .

FspHeaderFound:
  # Get the fsp TempRamInit Api address
  movl $FSP_HEADER_IMAGEBASE_OFFSET(%edi), %eax
  addl $FSP_HEADER_TEMPRAMINIT_OFFSET(%edi), %eax

  # Setup the hardcode stack
  movl $TempRamInitStack, %esp

  # Call the fsp TempRamInit Api
  jmp  *%eax

TempRamInitDone:
  cmp  $0x8000000E, %eax   #Check if EFI_NOT_FOUND returned. Error code for Microcode Update not found.
  je   CallSecFspInit      #If microcode not found, don't hang, but continue.

  cmp  $0x0, %eax
  jnz  FspApiFailed

  #   ECX: start of range
  #   EDX: end of range
CallSecFspInit:
  subl    PcdGet32 (PcdFspTemporaryRamSize), %edx # TemporaryRam for FSP
  xorl    %eax, %eax
  movl    %edx, %esp

  # Align the stack at DWORD
  addl  $3, %esp
  andl  $0xFFFFFFFC, %esp

  pushl   %edx
  pushl   %ecx
  pushl   %eax # zero - no hob list yet
  call    ASM_PFX(CallPeiCoreEntryPoint)

FspApiFailed:
  jmp .

.align 0x10
TempRamInitStack:
    .long  TempRamInitDone
    .long  FsptUpdDataPtr

#
# ROM-based Global-Descriptor Table for the Tiano PEI Phase
#
.align 0x10

#
# GDT[0]: 0x00: Null entry, never used.
#
.equ NULL_SEL,             . - GdtBase    # Selector [0]
GdtBase:
BootGdtTable: 
    .long  0
    .long  0

#
# Linear data segment descriptor
#
.equ LINEAR_SEL,           . - GdtBase     # Selector [0x8]
    .word  0xFFFF                          # limit 0xFFFFF
    .word  0                               # base 0
    .byte  0
    .byte  0x92                            # present, ring 0, data, expand-up, writable
    .byte  0xCF                            # page-granular, 32-bit
    .byte  0

#
# Linear code segment descriptor
#
.equ LINEAR_CODE_SEL,      . - GdtBase     # Selector [0x10]
    .word  0xFFFF                          # limit 0xFFFFF
    .word  0                               # base 0
    .byte  0
    .byte  0x9B                            # present, ring 0, data, expand-up, not-writable
    .byte  0xCF                            # page-granular, 32-bit
    .byte  0
#
# System data segment descriptor
#
.equ SYS_DATA_SEL,         . - GdtBase     # Selector [0x18]
    .word  0xFFFF                          # limit 0xFFFFF
    .word  0                               # base 0
    .byte  0
    .byte  0x93                            # present, ring 0, data, expand-up, not-writable
    .byte  0xCF                            # page-granular, 32-bit
    .byte  0

#
# System code segment descriptor
#
.equ SYS_CODE_SEL,         . - GdtBase     # Selector [0x20]
    .word  0xFFFF                          # limit 0xFFFFF
    .word  0                               # base 0
    .byte  0
    .byte  0x9A                            # present, ring 0, data, expand-up, writable
    .byte  0xCF                            # page-granular, 32-bit
    .byte  0
#
# Spare segment descriptor
#
.equ SYS16_CODE_SEL,       . - GdtBase     # Selector [0x28]
    .word  0xFFFF                          # limit 0xFFFFF
    .word  0                               # base 0
    .byte  0x0E                            # Changed from F000 to E000.
    .byte  0x9B                            # present, ring 0, code, expand-up, writable
    .byte  0x00                            # byte-granular, 16-bit
    .byte  0
#
# Spare segment descriptor
#
.equ SYS16_DATA_SEL,       . - GdtBase     # Selector [0x30]
    .word  0xFFFF                          # limit 0xFFFF
    .word  0                               # base 0
    .byte  0
    .byte  0x93                            # present, ring 0, data, expand-up, not-writable
    .byte  0x00                            # byte-granular, 16-bit
    .byte  0

#
# Spare segment descriptor
#
.equ SPARE5_SEL,           . - GdtBase     # Selector [0x38]
    .word  0                               # limit 0
    .word  0                               # base 0
    .byte  0
    .byte  0                               # present, ring 0, data, expand-up, writable
    .byte  0                               # page-granular, 32-bit
    .byte  0
.equ GDT_SIZE,             . - BootGdtTable    # Size, in bytes

#
# GDT Descriptor
#
GdtDesc:                                # GDT descriptor
    .word  GDT_SIZE - 1                    # GDT limit
    .long  BootGdtTable                    # GDT base address

ASM_PFX(ProtectedModeEntryLinearAddress):
ProtectedModeEntryLinearOffset:
  .long      ProtectedModeEntryPoint       # Offset of our 32 bit code
  .word      LINEAR_CODE_SEL
